#if !defined(ximera_physics_ode_joint_hpp)
#define ximera_physics_ode_joint_hpp

// -----------------------------------------------------------

#include <ximera/physics/ijoint.hpp>
#include <ximera/physics/ode/space.hpp>
#include <ximera/physics/ode/rigidbody.hpp>
#include <ximera/physics/ode/world.hpp>
#include <ximera/math/vector3.hpp>


namespace ximera { namespace physics { namespace ode
{
    class XIMERA_PLUGIN_API IOdeJoint
    {
    public:
        virtual void attachSpace(Space* space) = 0;
        virtual void detachSpace() = 0;

        virtual Space* space() const = 0;
        virtual dJointID jointID() const = 0;
    };

    template <typename Base>
    class XIMERA_PLUGIN_API JointBase : public Base, public IOdeJoint
    {
    public:
        typedef JointBase<Base> joint_base;

    public:
        JointBase();
        virtual ~JointBase();
        
        virtual void attach(IRigidBody* body0, IRigidBody* body1);
        virtual void detach();

        virtual IRigidBody* body0() const;
        virtual IRigidBody* body1() const;

        virtual void attachSpace(Space* space);
        virtual void detachSpace();

        virtual Space* space() const { return _space; }
        virtual dJointID jointID() const { return _jointID; }

    protected:
        Space* _space;
        dWorldID _worldID;
        dJointID _jointID;
        IRigidBody* _bodies[2];
    };

    class XIMERA_PLUGIN_API BallJoint : public JointBase<IBallJoint>
    {
    public:
        BallJoint();

        virtual math::Vector3 anchor0() const;
        virtual math::Vector3 anchor1() const;
        virtual void anchor(math::Vector3 const& value);

        virtual void attachSpace(Space* space);
    };

    class XIMERA_PLUGIN_API HingeJoint : public JointBase<IHingeJoint>
    {
    public:
        HingeJoint();

        virtual math::Vector3 anchor0() const;
        virtual math::Vector3 anchor1() const;
        virtual void anchor(math::Vector3 const& value);

        virtual math::Vector3 axis() const;
        virtual void axis(math::Vector3 const& value);

        virtual Real angle() const;

        virtual void attachSpace(Space* space);
    };

    class XIMERA_PLUGIN_API SliderJoint : public JointBase<ISliderJoint>
    {
    public:
        SliderJoint();

        virtual math::Vector3 axis() const;
        virtual void axis(math::Vector3 const& value);

        virtual math::Vector3 position() const;

        virtual void attachSpace(Space* space);
    };

    class XIMERA_PLUGIN_API UniversalJoint : public JointBase<IUniversalJoint>
    {
    public:
        UniversalJoint();

        virtual math::Vector3 anchor0() const;
        virtual math::Vector3 anchor1() const;
        virtual void anchor(math::Vector3 const& value);

        virtual math::Vector3 axis0() const;
        virtual void axis0(math::Vector3 const& value);

        virtual math::Vector3 axis1() const;
        virtual void axis1(math::Vector3 const& value);

        virtual void attachSpace(Space* space);
    };

    class XIMERA_PLUGIN_API Hinge2Joint : public JointBase<IHinge2Joint>
    {
    public:
        Hinge2Joint();

        virtual math::Vector3 anchor0() const;
        virtual math::Vector3 anchor1() const;
        virtual void anchor(math::Vector3 const& value);

        virtual math::Vector3 axis0() const;
        virtual void axis0(math::Vector3 const& value);

        virtual math::Vector3 axis1() const;
        virtual void axis1(math::Vector3 const& value);

        virtual void attachSpace(Space* space);
    };
}}}

#include <ximera/physics/ode/inline/joint.inl>

// -----------------------------------------------------------

#endif
